package core;

/**
 * A class representing a 16 bit ALU.
 * This ALU has:
 * 	- A register bank with 16 registers.
 * 	- Two Temporary registers for operations.
 * 	- One Temporary register for results.
 * 	- One Processor Status Register that is set at the end of each operation.
 * @author Ximo Tur ximotur at gmail dot com
 */
public class AluClass{
	private core.PsrClass psr = null;
	private short tempReg1 = 0;
	private short tempReg2 = 0;
	private short resReg = 0;
	private core.AluClass.Operations operation;
	private short C15 = 0;
	private boolean isSpecialSubCase = false;

	/**
	 * Constructor that sets the System's PSR (Processor Status Register)
	 * @param psr The System's PSR Object
	 */
	public AluClass(core.PsrClass psr){
		this.psr = psr;
	}
	
	private void setPSRFlags(int auxRes){
		if((auxRes & 0x0000FFFF) != 0){
			psr.setZ(0);
		}else{
			psr.setZ(1);
		}
		if((auxRes & 0x00008000) != 0){
			psr.setN(1);
		}else{
			psr.setN(0);
		}
		if((auxRes & 0x00010000) != 0){
			psr.setC(1);
		}else{
			psr.setC(0);
		}
		if(this.isSpecialSubCase){
			psr.setV(psr.getN());
		}else{
			psr.setV(this.C15 ^ psr.getC());
		}
	}

	//TODO: set all these methods to protected:

	/**
	 * Sets the the two temporary registers that will be used in the operation
	 * @param op1 The value for the first operator
	 * @param op2 The value for the second operator
	 */
	public void setOperands(short op1, short op2){
		this.tempReg1 = op1;
		this.tempReg2 = op2;
	}

	/**
	 * Returns the results of the last ALU operation
	 * @return the result.
	 */
	public short getResult(){
		return(this.resReg);
	}
	/**
	 * Sets the operation to be performed by the ALU.
	 * This operation is given by a value of the enum core.AluClass.Operation
	 * @param op is the operation to be performed
	 */
	public void setOperation(core.AluClass.Operations op){
		this.operation = op;
	}
	/**
	 * Calls the ALU to perform the operation
	 * The Operands must be previously set via the setOperands() method
	 * The Operation must be previously set via the setOperation() method
	 */
	private int makeSum(int a, int b){
		//Test the 15th bit carriage
		int auxA = (a & 0x00007FFF);
		int auxB = (b & 0x00007FFF);
		int auxRes = auxA + auxB;
		if((auxRes & 0x00008000) != 0){
			this.C15 = 1;
		}else{
			this.C15 = 0;
		}
		return(a + b);
	}
	public void performOperation(){
		int auxRes = 0;
		int aux1 = (((int)this.tempReg1) & 0x0000FFFF);
		int aux2 = (((int)this.tempReg2) & 0x0000FFFF);
		this.C15 = 0;
		this.isSpecialSubCase = false;
		switch(this.operation){
			case ADD:
				auxRes = makeSum(aux1, aux2);
				break;
			case ADC:
				aux2 = ((((int)this.tempReg2) + this.psr.getC()) & 0x0000FFFF);
				auxRes = makeSum(aux1, aux2);
				break;
			case CMP:
			case SUB:
				//Put the operand in 2nd complement:
				aux2 = ((-(int)this.tempReg2) & 0x0000FFFF);
				if(aux2 == 0x00008000){
					this.isSpecialSubCase = true;
				}
				auxRes = makeSum(aux1, aux2);
				break;
			case SBC:
				aux2 = ((-(((int)this.tempReg2) + this.psr.getC())) & 0x0000FFFF);
				if(aux2 == 0x00008000){
					this.isSpecialSubCase = true;
				}
				auxRes = makeSum(aux1, aux2);
				break;
			case INC:
				auxRes = makeSum(aux1, 0x00000001);
				break;
			case DEC:
				auxRes = makeSum(aux1, 0x0000FFFF);
				break;
			case AND:
				auxRes = aux1 & aux2;
				break;
			case OR:
				auxRes = aux1 | aux2;
				break;
			case XOR:
				auxRes = aux1 ^ aux2;
				break;
			case SHL:
				auxRes = aux1 << aux2;
				break;
			case SHR:
				auxRes = this.tempReg1 >> aux2;
				break;
			case NOT:
				auxRes = ~(aux2);
				break;
			case BIC:
				auxRes = aux1 & ~(aux2);
				break;
		}
		setPSRFlags(auxRes);
		this.resReg = (short)auxRes;
	}
	
	/**
	 * Call the ALU to perform an operation, shortened method
	 * @param op1 is the value for the first operator
	 * @param op2 is the value for the second operator
	 * @param op is the operation to be performed
	 * @return returns the result of the operation
	 */
	public short performOperation(short op1, short op2, core.AluClass.Operations op){
		this.setOperands(op1, op2);
		this.setOperation(op);
		performOperation();
		return(this.getResult());
	}

	/**
	 * Enumeration that describes the different Operations availables for this ALU.
	 * @author Ximo Tur ximotur at gmail dot com
	 *
	 */
	public enum Operations{
		ADD,
		ADC,
		SUB,
		SBC,
		INC,
		DEC,
		CMP,
		AND,
		OR,
		XOR,
		SHL,
		SHR,
		NOT,
		BIC
	}
}
